/*
    Copyright (C) 2012 Hugo Arregui, FuDePAN

    This file is part of Biopp.

    Biopp is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Biopp is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Biopp.  If not, see <http://www.gnu.org/licenses/>.
*/
#include <iostream>
#include <vector>
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <mili/mili.h>
#include "biopp/biopp.h"

using namespace std;
using namespace testing;
using namespace biopp;

typedef pair<char, char> Complements;

template <template <class> class A1, template<class> class A2>
void ASSERT_VALID_COMBINATION()
{
    ASSERT_TRUE((CombinationValid<A1, A2>::Valid));
    ASSERT_TRUE((CombinationValid<A2, A1>::Valid));
}

template <template <class> class A1, template<class> class A2>
void ASSERT_INVALID_COMBINATION()
{
    ASSERT_FALSE((CombinationValid<A1, A2>::Valid));
    ASSERT_FALSE((CombinationValid<A2, A1>::Valid));
}

template <class Alphabet>
void testComplements(const vector<Complements>& complements)
{
    for (vector<Complements>::const_iterator p = complements.begin(); p != complements.end(); ++p)
    {
        Alphabet alpha(p->first);
        alpha.complement();
        ASSERT_EQ(p->second, alpha.as_char());
        alpha.complement();
        ASSERT_EQ(p->first, alpha.as_char());
    }
}

template <class Alpha>
void testToChar(const char c, typename Alpha::Alpha e)
{
    Alpha alpha(e);
    ASSERT_EQ(c, alpha.as_char());
}

#define ASSERT_TO_CHAR(Alpha, c, e)   \
    testToChar< Alpha >(c, Alpha::e)

TEST(compound, as_char)
{
    typedef AUCG_Alphabet< Deletion_Alphabet<> > Compound;
    ASSERT_TO_CHAR(Compound, 'A', A);
    Compound alpha('-');
    ASSERT_EQ('-', alpha.as_char());
}

TEST(AUCG_Alphabet, as_char)
{
    ASSERT_TO_CHAR(AUCG_Alphabet<>, 'A', A);
    ASSERT_TO_CHAR(AUCG_Alphabet<>, 'U', U);
    ASSERT_TO_CHAR(AUCG_Alphabet<>, 'C', C);
    ASSERT_TO_CHAR(AUCG_Alphabet<>, 'G', G);
}

TEST(Deletion, as_char)
{
    ASSERT_TO_CHAR(Deletion_Alphabet<>, '-', Deletion);
}

TEST(DotDeletion, as_char)
{

    Deletion_Alphabet<> alpha('.');
    ASSERT_EQ('-', alpha.as_char());
}

TEST(ATCG_Alphabet, as_char)
{
    ASSERT_TO_CHAR(ATCG_Alphabet<>, 'A', A);
    ASSERT_TO_CHAR(ATCG_Alphabet<>, 'T', T);
    ASSERT_TO_CHAR(ATCG_Alphabet<>, 'C', C);
    ASSERT_TO_CHAR(ATCG_Alphabet<>, 'G', G);
}

TEST(AXCG_Alphabet, as_char)
{
    ASSERT_TO_CHAR(AXCG_Alphabet<>, 'A', A);
    ASSERT_TO_CHAR(AXCG_Alphabet<>, 'U', U);
    ASSERT_TO_CHAR(AXCG_Alphabet<>, 'C', C);
    ASSERT_TO_CHAR(AXCG_Alphabet<>, 'G', G);
}

TEST(Aminoacid_Standard_Alphabet, as_char)
{
    ASSERT_TO_CHAR(Aminoacid_Standard_Alphabet<>, 'A', A);
    ASSERT_TO_CHAR(Aminoacid_Standard_Alphabet<>, 'C', C);
    ASSERT_TO_CHAR(Aminoacid_Standard_Alphabet<>, 'D', D);
    ASSERT_TO_CHAR(Aminoacid_Standard_Alphabet<>, 'E', E);
    ASSERT_TO_CHAR(Aminoacid_Standard_Alphabet<>, 'F', F);
    ASSERT_TO_CHAR(Aminoacid_Standard_Alphabet<>, 'G', G);
    ASSERT_TO_CHAR(Aminoacid_Standard_Alphabet<>, 'H', H);
    ASSERT_TO_CHAR(Aminoacid_Standard_Alphabet<>, 'I', I);
    ASSERT_TO_CHAR(Aminoacid_Standard_Alphabet<>, 'K', K);
    ASSERT_TO_CHAR(Aminoacid_Standard_Alphabet<>, 'L', L);
    ASSERT_TO_CHAR(Aminoacid_Standard_Alphabet<>, 'M', M);
    ASSERT_TO_CHAR(Aminoacid_Standard_Alphabet<>, 'N', N);
    ASSERT_TO_CHAR(Aminoacid_Standard_Alphabet<>, 'P', P);        
    ASSERT_TO_CHAR(Aminoacid_Standard_Alphabet<>, 'Q', Q);
    ASSERT_TO_CHAR(Aminoacid_Standard_Alphabet<>, 'R', R);
    ASSERT_TO_CHAR(Aminoacid_Standard_Alphabet<>, 'S', S);
    ASSERT_TO_CHAR(Aminoacid_Standard_Alphabet<>, 'T', T);
    ASSERT_TO_CHAR(Aminoacid_Standard_Alphabet<>, 'V', V);
    ASSERT_TO_CHAR(Aminoacid_Standard_Alphabet<>, 'W', W);
    ASSERT_TO_CHAR(Aminoacid_Standard_Alphabet<>, 'Y', Y);
}

TEST(Aminoacid_StandardEx_Alphabet, as_char)
{
    Aminoacid_StandardEx_Alphabet<> alpha('A');
    ASSERT_EQ('A', alpha.as_char());

    alpha = 'C';
    ASSERT_EQ('C', alpha.as_char());

    alpha = 'D';
    ASSERT_EQ('D', alpha.as_char());

    alpha = 'E';
    ASSERT_EQ('E', alpha.as_char());

    alpha = 'F';
    ASSERT_EQ('F', alpha.as_char());

    alpha = 'G';
    ASSERT_EQ('G', alpha.as_char());

    alpha = 'H';
    ASSERT_EQ('H', alpha.as_char());

    alpha = 'I';
    ASSERT_EQ('I', alpha.as_char());

    alpha = 'K';
    ASSERT_EQ('K', alpha.as_char());

    alpha = 'L';
    ASSERT_EQ('L', alpha.as_char());

    alpha = 'M';
    ASSERT_EQ('M', alpha.as_char());

    alpha = 'N';
    ASSERT_EQ('N', alpha.as_char());

    alpha = 'P';
    ASSERT_EQ('P', alpha.as_char());        

    alpha = 'Q';
    ASSERT_EQ('Q', alpha.as_char());

    alpha = 'R';
    ASSERT_EQ('R', alpha.as_char());

    alpha = 'S';
    ASSERT_EQ('S', alpha.as_char());

    alpha = 'T';
    ASSERT_EQ('T', alpha.as_char());

    alpha = 'V';
    ASSERT_EQ('V', alpha.as_char());

    alpha = 'W';
    ASSERT_EQ('W', alpha.as_char());

    alpha = 'Y';
    ASSERT_EQ('Y', alpha.as_char());

    alpha = 'O';
    ASSERT_EQ('O', alpha.as_char());

    alpha = 'U';
    ASSERT_EQ('U', alpha.as_char());
}

TEST(PseudoAminoacid_Alphabet, as_char)
{
    ASSERT_TO_CHAR(PseudoAminoacid_Alphabet<>, 'B', B);
    ASSERT_TO_CHAR(PseudoAminoacid_Alphabet<>, 'J', J);
    ASSERT_TO_CHAR(PseudoAminoacid_Alphabet<>, 'X', X);
    ASSERT_TO_CHAR(PseudoAminoacid_Alphabet<>, 'Z', Z);
}

TEST(StopAminoacid_Alphabet, as_char)
{
    ASSERT_TO_CHAR(StopAminoacid_Alphabet<>, '*', STOP_CODON);
}


TEST(Aminoacid_Alphabet, as_char)
{
    Aminoacid_Alphabet<> alpha('A');
    ASSERT_EQ('A', alpha.as_char());

    alpha = 'C';
    ASSERT_EQ('C', alpha.as_char());

    alpha = 'D';
    ASSERT_EQ('D', alpha.as_char());

    alpha = 'E';
    ASSERT_EQ('E', alpha.as_char());

    alpha = 'F';
    ASSERT_EQ('F', alpha.as_char());

    alpha = 'G';
    ASSERT_EQ('G', alpha.as_char());

    alpha = 'H';
    ASSERT_EQ('H', alpha.as_char());

    alpha = 'I';
    ASSERT_EQ('I', alpha.as_char());

    alpha = 'K';
    ASSERT_EQ('K', alpha.as_char());

    alpha = 'L';
    ASSERT_EQ('L', alpha.as_char());

    alpha = 'M';
    ASSERT_EQ('M', alpha.as_char());

    alpha = 'N';
    ASSERT_EQ('N', alpha.as_char());

    alpha = 'P';
    ASSERT_EQ('P', alpha.as_char());        

    alpha = 'Q';
    ASSERT_EQ('Q', alpha.as_char());

    alpha = 'R';
    ASSERT_EQ('R', alpha.as_char());

    alpha = 'S';
    ASSERT_EQ('S', alpha.as_char());

    alpha = 'T';
    ASSERT_EQ('T', alpha.as_char());

    alpha = 'V';
    ASSERT_EQ('V', alpha.as_char());

    alpha = 'W';
    ASSERT_EQ('W', alpha.as_char());

    alpha = 'Y';
    ASSERT_EQ('Y', alpha.as_char());

    alpha = 'O';
    ASSERT_EQ('O', alpha.as_char());

    alpha = 'U';
    ASSERT_EQ('U', alpha.as_char());

    alpha = 'B';
    ASSERT_EQ('B', alpha.as_char());

    alpha = 'J';
    ASSERT_EQ('J', alpha.as_char());

    alpha = 'X';
    ASSERT_EQ('X', alpha.as_char());

    alpha = 'Z';
    ASSERT_EQ('Z', alpha.as_char());

    alpha = '*';
    ASSERT_EQ('*', alpha.as_char());

}




TEST(Alphabets, combinations)
{
    //invalid combinations    
    
    ASSERT_INVALID_COMBINATION<ATCG_Alphabet, ATCG_Alphabet>();
    ASSERT_INVALID_COMBINATION<ATCG_Alphabet, AUCG_Alphabet>();
    ASSERT_INVALID_COMBINATION<ATCG_Alphabet, AXCG_Alphabet>();
    ASSERT_INVALID_COMBINATION<ATCG_Alphabet, Aminoacid_Alphabet>();
    ASSERT_INVALID_COMBINATION<ATCG_Alphabet, Aminoacid_Standard_Alphabet>();
    ASSERT_INVALID_COMBINATION<ATCG_Alphabet, Aminoacid_StandardEx_Alphabet>();
    ASSERT_INVALID_COMBINATION<ATCG_Alphabet, PseudoAminoacid_Alphabet>();
    ASSERT_INVALID_COMBINATION<ATCG_Alphabet, StopAminoacid_Alphabet>();

    ASSERT_INVALID_COMBINATION<AUCG_Alphabet, AUCG_Alphabet>();
    ASSERT_INVALID_COMBINATION<AUCG_Alphabet, AXCG_Alphabet>();
    ASSERT_INVALID_COMBINATION<AUCG_Alphabet, Aminoacid_Alphabet>();
    ASSERT_INVALID_COMBINATION<AUCG_Alphabet, Aminoacid_Standard_Alphabet>();
    ASSERT_INVALID_COMBINATION<AUCG_Alphabet, Aminoacid_StandardEx_Alphabet>();
    ASSERT_INVALID_COMBINATION<AUCG_Alphabet, PseudoAminoacid_Alphabet>();
    ASSERT_INVALID_COMBINATION<AUCG_Alphabet, StopAminoacid_Alphabet>();

    ASSERT_INVALID_COMBINATION<AXCG_Alphabet, AXCG_Alphabet>();
    ASSERT_INVALID_COMBINATION<AXCG_Alphabet, Aminoacid_Alphabet>();
    ASSERT_INVALID_COMBINATION<AXCG_Alphabet, Aminoacid_Standard_Alphabet>();
    ASSERT_INVALID_COMBINATION<AXCG_Alphabet, Aminoacid_StandardEx_Alphabet>();
    ASSERT_INVALID_COMBINATION<AXCG_Alphabet, PseudoAminoacid_Alphabet>();
    ASSERT_INVALID_COMBINATION<AXCG_Alphabet, StopAminoacid_Alphabet>();

    ASSERT_INVALID_COMBINATION<Pseudo_Alphabet, Pseudo_Alphabet>();
    ASSERT_INVALID_COMBINATION<Pseudo_Alphabet, Aminoacid_Alphabet>();
    ASSERT_INVALID_COMBINATION<Pseudo_Alphabet, Aminoacid_Standard_Alphabet>();
    ASSERT_INVALID_COMBINATION<Pseudo_Alphabet, Aminoacid_StandardEx_Alphabet>();
    ASSERT_INVALID_COMBINATION<Pseudo_Alphabet, PseudoAminoacid_Alphabet>();
    ASSERT_INVALID_COMBINATION<Pseudo_Alphabet, StopAminoacid_Alphabet>();

    ASSERT_INVALID_COMBINATION<Aminoacid_Alphabet, Aminoacid_Standard_Alphabet>();
    ASSERT_INVALID_COMBINATION<Aminoacid_Alphabet, Aminoacid_StandardEx_Alphabet>();
    ASSERT_INVALID_COMBINATION<Aminoacid_Alphabet, PseudoAminoacid_Alphabet>();
    ASSERT_INVALID_COMBINATION<Aminoacid_Alphabet, StopAminoacid_Alphabet>();

    ASSERT_INVALID_COMBINATION<Aminoacid_Standard_Alphabet, Aminoacid_StandardEx_Alphabet>();

    //Valid combinations

    ASSERT_VALID_COMBINATION<ATCG_Alphabet, Deletion_Alphabet>();
    ASSERT_VALID_COMBINATION<ATCG_Alphabet, Pseudo_Alphabet>();

    ASSERT_VALID_COMBINATION<AUCG_Alphabet, Deletion_Alphabet>();
    ASSERT_VALID_COMBINATION<AUCG_Alphabet, Pseudo_Alphabet>();

    ASSERT_VALID_COMBINATION<AXCG_Alphabet, Deletion_Alphabet>();
    ASSERT_VALID_COMBINATION<AXCG_Alphabet, Pseudo_Alphabet>();

    ASSERT_VALID_COMBINATION<Pseudo_Alphabet, Deletion_Alphabet>();

    ASSERT_VALID_COMBINATION<Aminoacid_Alphabet, Deletion_Alphabet>();

    ASSERT_VALID_COMBINATION<Aminoacid_Standard_Alphabet, Deletion_Alphabet>();
    ASSERT_VALID_COMBINATION<Aminoacid_Standard_Alphabet, PseudoAminoacid_Alphabet>();
    ASSERT_VALID_COMBINATION<Aminoacid_Standard_Alphabet, StopAminoacid_Alphabet>();

    ASSERT_VALID_COMBINATION<Aminoacid_StandardEx_Alphabet, Deletion_Alphabet>();
    ASSERT_VALID_COMBINATION<Aminoacid_StandardEx_Alphabet, PseudoAminoacid_Alphabet>();
    ASSERT_VALID_COMBINATION<Aminoacid_StandardEx_Alphabet, StopAminoacid_Alphabet>();
}

TEST(AUCG_Alphabet, complement)
{
    vector<Complements> complements;
    insert_into(complements, Complements('U', 'A'));
    insert_into(complements, Complements('C', 'G'));
    testComplements<AUCG_Alphabet<> >(complements);
}

TEST(ATCG_Alphabet, complement)
{
    vector<Complements> complements;
    insert_into(complements, Complements('T', 'A'));
    insert_into(complements, Complements('C', 'G'));
    testComplements<ATCG_Alphabet<> >(complements);
}

TEST(AXCG_Alphabet, complement)
{
    vector<Complements> complements;
    insert_into(complements, Complements('U', 'A'));
    insert_into(complements, Complements('C', 'G'));
    testComplements<AXCG_Alphabet<> >(complements);
}

TEST(Pseudo_Alphabet, complement)
{
    vector<Complements> complements;
    insert_into(complements, Complements('R', 'Y'));
    insert_into(complements, Complements('K', 'M'));
    insert_into(complements, Complements('S', 'S'));
    insert_into(complements, Complements('W', 'W'));
    insert_into(complements, Complements('B', 'V'));
    insert_into(complements, Complements('H', 'D'));
    insert_into(complements, Complements('N', 'N'));
    testComplements<Pseudo_Alphabet<> >(complements);
}
